﻿// Copyright (c) Microsoft Corporation.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.Metadata.Edm;
using System.Data.EntityClient;
using System.Data.Common;
using System.Collections.ObjectModel;
using System.Xml.Linq;

namespace EdmMetadataHelpers
{
    /// <summary>
    /// EntityDataReaderAdapter is a utility class for moving records 
    /// in an EntityDataReader into a DataTable.
    /// </summary>
    public class EdmDataReaderAdapter
    {
        /// <summary>
        /// We hold onto an ObjectItemCollection because this 
        /// gives us access to the EF's primitive types
        /// </summary>
        readonly static ObjectItemCollection _oc = new ObjectItemCollection();
        /// <summary>
        /// Loads a DataTable from the contents of an EntityDataReader
        /// </summary>
        /// <param name="reader">an EntityDataReader</param>
        /// <returns>a DataTable</returns>
        public static DataTable LoadTable(EntityDataReader reader) 
        {
            DataTable table = LoadSchema(reader.DataRecordInfo);
            table.FillWith(reader.Enumerate());
            return table;
        }
        /// <summary>
        /// Initializes a DataTables schema from a DataRecordInfo 
        /// 
        /// a DataRecordInfo hangs of an EntityDataReader.DataRecordInfo
        /// </summary>
        /// <param name="info">a DataRecordInfo holding metadata about the shape of an EntityDataReader</param>
        /// <returns>a DataTable</returns>
        public static DataTable LoadSchema(DataRecordInfo info)
        {
            DataTable table = new DataTable(info.RecordType.EdmType.FullName);
            var columns = info.FieldMetadata
                        .Select(field => ConvertToColumn(field))
                        .Where(column => column != null);
            foreach (DataColumn column in columns)
                table.Columns.Add(column);
            return table;
        }
   
        /// <summary>
        /// Converts a FieldMetadata into the appropriate DataColumn
        /// for constructing a DataTable
        /// </summary>
        /// <param name="field">metadata about a </param>
        /// <returns>a DataColumn or null if the FieldMetadata can't be converted</returns>
        protected static DataColumn ConvertToColumn(FieldMetadata field)
        {
            //Work out the CLR type associated with the EdmType
            EdmType type = field.FieldType.TypeUsage.EdmType;
            var clrTypes = from p in _oc.GetPrimitiveTypes()
                           where p.GetEdmPrimitiveType().FullName == type.FullName
                           select p.ClrEquivalentType;

            Type clrType = clrTypes.FirstOrDefault();

            //This is to ensure we don't attempt to lift row(s). At least not in this version.
            if (clrType == null) return null;

            //Construct a column
            DataColumn column = new DataColumn(field.FieldType.Name, clrType);

            // Use the fields TypeUsage facets to see if there is a maxlength
            // for any string columns
            if (clrType == typeof(string))
            {
                Facet maxLength = GetFacet(field, "MaxLength");
                if (maxLength != null) column.MaxLength = (int) maxLength.Value;
            }
            return column;
        }
        /// <summary>
        /// Gets the requested Facet from the FieldMetadata
        /// </summary>
        /// <param name="field">the FieldMetadata whose facets we want to search</param>
        /// <param name="name">the name of the facet to look for</param>
        /// <returns>a facet or NULL if not found</returns>
        protected static Facet GetFacet(FieldMetadata field, string name)
        {
            Facet facet = null;
            field.FieldType.TypeUsage.Facets.TryGetValue(name, true, out facet);
            return facet;
        }
    }
}
